/**
 * @author Huy HCQ
 */
package pit.com.ordermobilesystem.http;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.http.NameValuePair;
import org.apache.http.client.params.HttpClientParams;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.scheme.SocketFactory;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.protocol.HTTP;




abstract public class BaseHttpApi implements HttpApi {
  protected static final Logger LOG = Logger.getLogger(BaseHttpApi.class.getCanonicalName());
  private boolean DEBUG=false;
  protected static final int TIMEOUT = 60;

  private final DefaultHttpClient mHttpClient;

  public BaseHttpApi() {
    mHttpClient = null;
  }

  public BaseHttpApi(DefaultHttpClient httpClient) {
    mHttpClient = httpClient;
  }

  public HttpURLConnection createHttpURLConnection(URL url) throws IOException {
    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
    conn.setDoInput(true);
    // conn.setDoOutput(true);
    conn.setUseCaches(false);
    conn.setConnectTimeout(TIMEOUT * 1000);
    conn.setRequestMethod("GET");
    conn.setRequestProperty("Connection", "Keep-Alive");
    conn.setRequestProperty("Content-Type", "text/plain; charset=utf-8");
    conn.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 6.1; rv:13.0) Gecko/20100101 Firefox/13.0.1");
    return conn;
  }

  public HttpModel executeHttpRequest(String sUrl, Parser<? extends HttpModel> parser, ResponseType type)
      throws HttpApiException, IOException {
    return executeHttpRequestContent(sUrl, "", parser, type);
  }// end sendHttpPost( String )

  public HttpModel executeHttpRequestContent(String sUrl, String content, Parser<? extends HttpModel> parser,
      ResponseType type) throws HttpApiException, IOException {

    URL url = new URL(sUrl);
    StringBuilder response = new StringBuilder();
    HttpURLConnection conn = createHttpURLConnection(url);
    //conn.setRequestProperty("Accept-Encoding", "gzip");
    DataOutputStream dos = null;
    BufferedReader in = null;
    try {
      if (!content.equals("")) {
        conn.setRequestMethod("POST");
        conn.setDoOutput(true);
        dos = new DataOutputStream(conn.getOutputStream());
        byte[] request_body = content.getBytes();
        // send request string to server
        for (int i = 0; i < request_body.length; i++) {
          dos.writeByte(request_body[i]);
        }
      }

      int statusCode = conn.getResponseCode();
      switch (statusCode) {
      case 200:
      case 204:
        in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));
        String responseLine = "";
        while ((responseLine = in.readLine()) != null) {
          response.append(responseLine);
        }
        in.close();
        // return response.toString();
        // String content = EntityUtils.toString(response.getEntity());
        if (type.equals(ResponseType.JSON))
          return JSONUtils.consume(parser, response.toString());
        else	
          return StringUtils.consume(parser, response.toString());
      case 400:
        if (DEBUG)
          LOG.log(Level.FINE, "HTTP Code: 400");
        throw new HttpApiException(conn.getResponseMessage());
      case 401:
        // response.getEntity().consumeContent();
        if (DEBUG)
          LOG.log(Level.FINE, "HTTP Code: 401");
        throw new HttpApiException(conn.getResponseMessage());
      case 404:
        if (DEBUG)
          LOG.log(Level.FINE, "HTTP Code: 404");
        throw new HttpApiException(conn.getResponseMessage());
      case 500:
        if (DEBUG)
          LOG.log(Level.FINE, "HTTP Code: 500");
        throw new HttpApiException("MobiPlace is down. Try again later.");
      default:
        if (DEBUG)
          LOG.log(Level.FINE, "Error connecting to AyamBrand: " + Integer.toString(statusCode));
        throw new HttpApiException("Error connecting to AyamBrand: " + statusCode + ". Try again later.");
      }
      // obtain DataInputStream for receiving server response
    } finally {
      if (dos != null)
        dos.close();
      if (in != null)
        in.close();
      if (conn != null)
        conn.disconnect();
    }
  }// end sendHttpPost( String )

  /**
   * Create a thread-safe client. This client does not do redirecting, to allow
   * us to capture correct "error" codes.
   * 
   * @return HttpClient
   */
  public static final DefaultHttpClient createHttpClient() {
    // Sets up the http part of the service.
    final SchemeRegistry supportedSchemes = new SchemeRegistry();

    // Register the "http" protocol scheme, it is required
    // by the default operator to look up socket factories.
    final SocketFactory sf = PlainSocketFactory.getSocketFactory();
    supportedSchemes.register(new Scheme("http", sf, 80));
    supportedSchemes.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), 443));

    // Set some client http client parameter defaults.
    final HttpParams httpParams = createHttpParams();
    HttpClientParams.setRedirecting(httpParams, false);

    final ClientConnectionManager ccm = new ThreadSafeClientConnManager(httpParams, supportedSchemes);
    return new DefaultHttpClient(ccm, httpParams);
  }

  /**
   * Create the default HTTP protocol parameters.
   */
  private static final HttpParams createHttpParams() {
    final HttpParams params = new BasicHttpParams();

    // Turn off stale checking. Our connections break all the time anyway,
    // and it's not worth it to pay the penalty of checking every time.
    HttpConnectionParams.setStaleCheckingEnabled(params, false);

    HttpConnectionParams.setConnectionTimeout(params, TIMEOUT * 1000);
    HttpConnectionParams.setSoTimeout(params, TIMEOUT * 1000);
    HttpConnectionParams.setSocketBufferSize(params, 8192);

    return params;
  }

  private List<NameValuePair> stripNulls(NameValuePair... nameValuePairs) {
    List<NameValuePair> params = new ArrayList<NameValuePair>();
    for (NameValuePair param : nameValuePairs) {
      if (param.getValue() != null) {
        if (DEBUG)
          LOG.log(Level.FINE, "Param: " + param);
        params.add(param);
      }
    }
    return params;
  }

  /**
   * Create Http GET link with encode formatted.
   */
  public String createHttpGet(String url, NameValuePair... nameValuePairs) throws UnsupportedEncodingException {
    if (DEBUG)
      LOG.log(Level.FINE, "creating HttpGet for: " + url);
    //String query = BaseHttpApi.urlFormat("&", stripNulls(nameValuePairs));
    String query = URLEncodedUtils.format(stripNulls(nameValuePairs), HTTP.UTF_8);
    if (DEBUG)
      LOG.log(Level.FINE, "Created: " + url + "?" + query);
    if (query.equals(""))
      return url;
    else
      return (url + "?" + query);
  }

  /**
   * Create Http REST link with encode formatted.
   */
  public String createHttpRest(String url, NameValuePair... nameValuePairs) throws UnsupportedEncodingException {
    if (DEBUG)
      LOG.log(Level.FINE, "creating HttpGet for: " + url);
    String query = BaseHttpApi.urlFormat("/", stripNulls(nameValuePairs));
    if (DEBUG)
      LOG.log(Level.FINE, "Created: " + url + "/" + query);
    if (query.equals(""))
      return url;
    else
      return (url + "/" + query);
  }

  private static final String urlFormat(String seperate, List<NameValuePair> list) throws UnsupportedEncodingException {
    String result = "";
    for (NameValuePair val : list) {
    	//add by nthuyen follow aHuy's request
      result += seperate + URLEncoder.encode(val.getValue(), HTTP.UTF_8).replaceAll("\\+", "%20");
    
    }
    if (result.equals(""))
      return result;
    else
      return result.substring(1);
    
    
  }
}
